package com.yunbiao.communication;

import android.util.Log;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Registration;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.provider.ProviderManager;

public class Xmpp {
    private static final String XMPP_RESOURCE_NAME = "AndroidpnClient";
    private String host;
    private int port;
    private String userName;
    private String password;

    private ConnectionListener mConnectionListener;

    private NotificationPacketListener mNotificationPacketListener;

    private final XmppTask xmppTask;

    private Xmpp(Builder builder){
        this.host = builder.host;
        this.port = builder.port;
        this.userName = builder.userName;
        this.password = userName + "_" + android.os.Build.MODEL.replaceAll("_", "");
        xmppTask = new XmppTask();
    }



    private class XmppTask{

        private XMPPConnection mConnection;
        private Registration mRegistration;

        private void connect(Runnable runnable){
            d("connecting...");
            if(mConnection != null && mConnection.isConnected()){
                d("is connected");
                runnable.run();
                return;
            }
            ConnectionConfiguration connConfig = new ConnectionConfiguration(host,port);
            connConfig.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
            connConfig.setSASLAuthenticationEnabled(false);
            connConfig.setCompressionEnabled(false);
            connConfig.setDebuggerEnabled(false);

            XMPPConnection connection = new XMPPConnection(connConfig);
            mConnection = connection;
            try {
                connection.connect();
                d("connect success");

                ProviderManager.getInstance().addIQProvider("notification", "androidpn:iq:notification", new NotificationIQProvider());

                runnable.run();
            } catch (Exception e){
                e("connect error! ",e);
            }
        }

        private void register(Runnable runnable){
            d("registering...");
            if(mRegistration != null && mRegistration.isRegistered()){
                d("register success");
                runnable.run();
                return;
            }

            Registration registration = new Registration();
            PacketFilter packetFilter = new AndFilter(new PacketIDFilter(
                    registration.getPacketID()), new PacketTypeFilter(
                    IQ.class));
            PacketListener packetListener = packet -> {
                if(packet instanceof IQ){
                    IQ response = (IQ) packet;
                    if(response.getType() == IQ.Type.ERROR){
                        if (!response.getError().toString().contains("409")) {
                            e( "register unknown error! " + response.getError().getCondition());
                        } else {
                            e("register error! ",response.getError());
                        }
                    } else if(response.getType() == IQ.Type.RESULT){
                        d("register success");
                        runnable.run();
                    }
                }
            };
            mConnection.addPacketListener(packetListener,packetFilter);

            d("register username=" + userName);
            d("register password=" + password);

            registration.setType(IQ.Type.SET);
            registration.addAttribute("username",userName);
            registration.addAttribute("password",password);
            mRegistration = registration;
            mConnection.sendPacket(registration);
        }

        private void login(Runnable runnable){
            d("login...");
            if(mConnection != null && mConnection.isConnected() && mConnection.isAuthenticated()){
                d("login success");
                runnable.run();
                return;
            }

            try {
                mConnection.login(userName,password,XMPP_RESOURCE_NAME);
                d("login success");

                mConnection.addConnectionListener(mConnectionListener);

                // packet filter
                PacketFilter packetFilter = new PacketTypeFilter(NotificationIQ.class);
                // packet listener
                mConnection.addPacketListener(mNotificationPacketListener, packetFilter);

            } catch (XMPPException e) {
                e("login error! ",e);
                String errMsg = e.getMessage();
                if(errMsg != null && errMsg.contains("401")){
                    register(() -> login(runnable));
                    return;
                }
            }


        }
    }

    public static class Builder{
        private String host;
        private int port;
        private String userName;

        public Builder setHost(String host) {
            this.host = host;
            return this;
        }

        public Builder setPort(int port) {
            this.port = port;
            return this;
        }

        public Builder setUserName(String userName) {
            this.userName = userName;
            return this;
        }

        public Xmpp build(){
            return new Xmpp(this);
        }

    }

    private void d(String message){
        Log.d(TAG, message);
    }

    private void e(String message){
        Log.e(TAG, message);
    }

    private void e(String message,Throwable e){
        if(e != null){
            Log.e(TAG, message + "(" + e.getMessage() + ")");
        } else {
            e(message);
        }
    }

    private void e(String message, XMPPError e){
        if(e != null){
            Log.e(TAG, message + "(" + e.getMessage() + ")");
        } else {
            e(message);
        }
    }

    private static final String TAG = "Xmpp";
}
